﻿using System;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using DotNetNuke;
using DotNetNuke.Entities.Host;
using DotNetNuke.Entities.Tabs;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Data;
using DotNetNuke.Entities.Modules.Actions;
using DotNetNuke.Entities.Users;
using DotNetNuke.Security;
using DotNetNuke.UI.UserControls;
using DotNetNuke.Framework;
using DotNetNuke.Services.Search;

using System.Security.Permissions;
using System.Net;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.IO;
using System.Data;
using System.Text.RegularExpressions;

//localization
using DotNetNuke.Services.Localization;

namespace Findy.XsltDb
{

    public partial class DesktopModules_XsltDb_XsltDbHost : System.Web.UI.UserControl, IXsltDbHost
    {

        /// //////////////////////////////////////////////////////////
        PortalModuleBase getModule()
        {
            Control p = this.Parent;
            while (p != null)
                if (p is PortalModuleBase)
                    return p as PortalModuleBase;
                else
                    p = p.Parent;
            return null;
        }
        UserInfo UserInfo
        {
            get
            {
                PortalModuleBase pmb = getModule();
                if (pmb != null)
                    return pmb.UserInfo;
                else
                    return UserController.GetCurrentUserInfo();
            }
        }
        /// //////////////////////////////////////////////////////////

        private string explicitConfigID;
        public string ConfigurationID
        {
            set { explicitConfigID = value; }
        }

        private int explicitModuleID = -1;
        public int ModuleID
        {
            set { explicitModuleID = value; }
        }

        private string explicitAlias;
        public string Alias
        {
            set { explicitAlias = value; }
        }

        private string context = string.Empty;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public string XContext {
            get { return context; }
            set { context = value; }
        }


        static Regex texteditor = new Regex(@"<texteditor[^>]*>.*?</texteditor>", RegexOptions.Singleline | RegexOptions.Compiled);
        static Regex mdoAsp = new Regex(@"</?mdo:asp[^>]*>", RegexOptions.Singleline | RegexOptions.Compiled);
        string xml = @"<root/>";
        string resourcePath = "~/DesktopModules/XsltDb/App_LocalResources/ViewXsltDb.ascx";

        XsltDbUtils.aConfig config;

        Stopwatch initTimer, preTimer, renderTimer;

        protected void Page_Init(object sender, EventArgs e)
        {
            initTimer = Stopwatch.StartNew();

            try
            {
                jQuery.RequestRegistration();
                string html = string.Empty;
                try
                {
                    if (explicitConfigID != null)
                        config = XsltDbUtils.GetConfigByID(explicitConfigID.ToLower());
                    else if (explicitAlias != null)
                        config = XsltDbUtils.GetConfigByService(explicitAlias.ToLower());
                    else if (explicitModuleID != -1)
                        config = XsltDbUtils.GetConfig(explicitModuleID);
                    else
                        config = XsltDbUtils.GetConfig(getModule().ModuleId, getModule().TabId);
                    if (config != null && config.ActiveXslt.Length > 0)
                    {
                        Transformer t = new Transformer(getModule(), config.ModuleID);
                        t.TrueHost = this;
                        t.Config = config;
                        html = t.Transform(xml, config.ActiveXslt, config.IsSuper);
                    }
                    else
                        if (UserInfo.IsInRole("Administrators") || UserInfo.IsSuperUser)
                            html = XsltDbResX.Localize("EmptyMessage.Text", resourcePath);
                }
                catch (Exception ex)
                {
                    RedirectException rex = ex.InnerException as RedirectException;
                    if (rex != null)
                    {
                        if (rex.IsTransfer)
                            Server.TransferRequest(rex.URL, true);
                        else
                            Response.Redirect((ex.InnerException as RedirectException).URL, false);
                        return;
                    }
                    if (UserInfo.IsInRole("Administrators") || UserInfo.IsSuperUser)
                        html = XsltDbUtils.GetExceptionMessage(ex);
                    XsltDbUtils.WriteLog(ex);
                }
                html = mdoAsp.Replace(html, string.Empty);
                string[] htmls = texteditor.Split(html);
                if (htmls.Length > 1)
                {
                    MatchCollection ms = texteditor.Matches(html);
                    //System.Diagnostics.Debugger.Break();

                    for (int i = 0; i < htmls.Length; i++)
                    {
                        if (i > 0)
                        {
                            XPathNavigator doc = new XPathDocument(new StringReader(ms[i - 1].Value)).CreateNavigator();

                            switch (doc.SelectSingleNode("/*").Name)
                            {
                                case "texteditor":
                                    {
                                        TextEditor te = (TextEditor)LoadControl("~/controls/TextEditor.ascx");
                                        foreach (XPathNavigator a in doc.Select("/texteditor/@*"))
                                        {
                                            switch (a.Name)
                                            {
                                                case "width": te.Width = new Unit(a.Value); break;
                                                case "height": te.Height = new Unit(a.Value); break;
                                                case "id": te.ID = a.Value; break;
                                                case "choose-mode": te.ChooseMode = a.Value == "true"; break;
                                                case "mode": te.Mode = a.Value; break;
                                            }
                                        }
                                        Controls.Add(te);
                                        te.HtmlEncode = false;
                                        te.Text = doc.Value;
                                    } break;
                            }
                        }
                        AppendControls(htmls[i]);
                    }
                }
                else
                    AppendControls(html);
            }
            catch (Exception ex)
            {
                if (UserInfo.IsInRole("Administrators") || UserInfo.IsSuperUser)
                    Controls.Add(new LiteralControl(XsltDbUtils.GetExceptionMessage(ex)));
                XsltDbUtils.WriteLog(ex);
            }

            RegisterHeaders();
            RegisterIDs();

            initTimer.Stop();
        }

        struct ModID
        {
            public int TabID;
            public int ModuleID;
            public int TabModuleID;
            public string ClientID;
            public string ConfigID;
        }

        static Regex TelerikRegex = new Regex(@"<\s*telerik:", RegexOptions.Singleline | RegexOptions.Compiled);
        static Regex DnnRegex = new Regex(@"<\s*dnn:", RegexOptions.Singleline | RegexOptions.Compiled );
        static Regex RunAtRegex = new Regex(@"(?<=<[^>]+?)runat\s*=\s*""server""", RegexOptions.Singleline | RegexOptions.Compiled);

        void AppendControls(string ctrls)
        {
            StringBuilder sb = new StringBuilder();

            if (DnnRegex.IsMatch(ctrls))
                sb.Append("<%@ Register TagPrefix=\"dnn\" Assembly=\"DotNetNuke\" Namespace=\"DotNetNuke.UI.WebControls\"%>");

            if ( TelerikRegex.IsMatch(ctrls) )
                sb
                    .Append("<%@ Register TagPrefix=\"telerik\" Namespace=\"Telerik.Web.UI\" Assembly=\"Telerik.Web.UI\" %>")
                    .Append("<%@ Register TagPrefix=\"telerik\" Namespace=\"Telerik.Charting\" Assembly=\"Telerik.Web.UI\" %>");

            if (sb.Length > 0 || RunAtRegex.IsMatch(ctrls))
                Controls.Add(ParseControl(sb.ToString() + ctrls));
            else
                Controls.Add(new LiteralControl(sb.ToString() + ctrls));
        }

        void RegisterIDs()
        {
            List<ModID> ht = (List<ModID>)Context.Items["ModulesOnPage"];
            if (ht == null)
            {
                ht = new List<ModID>();
                Context.Items["ModulesOnPage"] = ht;
            }
            if (getModule() != null)
                ht.Add(new ModID { 
                    TabID = getModule().TabId,
                    ModuleID = getModule().ModuleId,
                    TabModuleID = getModule().TabModuleId,
                    ClientID = Transformer.BuildClientID(this, getModule()),
                    ConfigID = config != null ? config.ConfigID : string.Empty
                });
            else
                ht.Add(new ModID {
                    TabID = TabController.CurrentPage.TabID, ModuleID = config.ModuleID, TabModuleID = -1,
                    ClientID = Transformer.BuildClientID(this, getModule()),
                    ConfigID = config != null ? config.ConfigID : string.Empty
                });
        }

        void RegisterHeaders()
        {
            if (Context.Items["XsltDbGlobalScript"] == null)
            {
                Context.Items["XsltDbGlobalScript"] = true;

                string appPath = Request.ApplicationPath;
                if (appPath == "/") appPath = "";

                var PortalSettings = PortalController.GetCurrentPortalSettings();

                DesktopModuleInfo dmi = DesktopModuleController.GetDesktopModuleByModuleName("XsltDb", PortalSettings.PortalId);

                Page.Header.Controls.Add(new LiteralControl(string.Format(
                    @"<script type=""text/javascript"">
var mdo_ApplicationPath = {0};
var mdo_PortalAlias = {1};
var mdo_SkinPath = {2};
</script>",
          Transformer.EncodeJsString(appPath),
          PortalSettings.PortalAlias.HTTPAlias.Contains("/") ? Transformer.EncodeJsString(PortalSettings.PortalAlias.HTTPAlias) : "null",
          Transformer.EncodeJsString(DotNetNuke.UI.Skins.Skin.GetSkin(this.Page as PageBase).SkinPath)
          )));

                Page.Header.Controls.Add(new LiteralControl(
                    "<script type=\"text/javascript\" src=\"" + appPath
                    + "/DesktopModules/XsltDb/xsltdb.js?v=" + dmi.Version
                    + "\"></script>"));
            }
        }

        void XsltDbProcessControlsRecursive(Control c)
        {
            if (c is SqlDataSource)
            {
                ((SqlDataSource)c).Selected += new SqlDataSourceStatusEventHandler(ViewXsltDb_SqlCommand);
                ((SqlDataSource)c).Selecting += new SqlDataSourceSelectingEventHandler(ViewXsltDb_Selecting);

                ((SqlDataSource)c).Inserted += new SqlDataSourceStatusEventHandler(ViewXsltDb_SqlCommand);
                ((SqlDataSource)c).Inserting += new SqlDataSourceCommandEventHandler(ViewXsltDb_Updating);

                ((SqlDataSource)c).Updated += new SqlDataSourceStatusEventHandler(ViewXsltDb_SqlCommand);
                ((SqlDataSource)c).Updating += new SqlDataSourceCommandEventHandler(ViewXsltDb_Updating);

                ((SqlDataSource)c).Deleted += new SqlDataSourceStatusEventHandler(ViewXsltDb_SqlCommand);
                ((SqlDataSource)c).Deleting += new SqlDataSourceCommandEventHandler(ViewXsltDb_Updating);

                if (config == null || !config.IsSuper)
                    SqlDataSourceLiteral.Text = "<p style=\"color:red;font-weight:bold;\">the module must be a super module to use SqlDataSource</p>";

                return;
            }

            //c.DataBind();

            foreach (Control cc in c.Controls)
                XsltDbProcessControlsRecursive(cc);
        }

        void ViewXsltDb_Updating(object sender, SqlDataSourceCommandEventArgs e)
        {
            if (config == null || !config.IsSuper)
                e.Cancel = true;
        }

        void ViewXsltDb_Selecting(object sender, SqlDataSourceSelectingEventArgs e)
        {
            if (config == null || !config.IsSuper)
                e.Cancel = true;
        }

        void ViewXsltDb_SqlCommand(object sender, SqlDataSourceStatusEventArgs e)
        {
            if (e.Exception != null)
            {
                SqlDataSourceLiteral.Text = XsltDbUtils.GetExceptionMessage(e.Exception);
                e.ExceptionHandled = true;
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            preTimer = Stopwatch.StartNew();
            try
            {
                if (Context.Items["ModRepositoryCreated"] == null)
                {
                    Context.Items["ModRepositoryCreated"] = true;
                    List<string> lines = new List<string>();
                    foreach (ModID mi in (List<ModID>)Context.Items["ModulesOnPage"])
                        lines.Add(string.Format("'{0}': {{ 'TabID' : {1}, 'ModuleID' : {2}, 'TabModuleID': {3}, 'ConfigID': '{4}' }}",
                            mi.ClientID, mi.TabID, mi.ModuleID, mi.TabModuleID,  mi.ConfigID));
                    string json = "var XsltDbModRepository = {" + string.Join(",", lines.ToArray()) + "};";
                    Page.Header.Controls.Add(new LiteralControl("<script type=\"text/javascript\">"
                        + json + "</script>"));
                }
                XsltDbProcessControlsRecursive(this);
                DataBind();
                if (config != null)
                {
                    Transformer t = new Transformer(getModule());
                    t.TrueHost = this;
                    t.Config = config;
                    t.PreRender(config.ActiveXslt, xml, config.IsSuper);
                }

                base.OnPreRender(e);
                preTimer.Stop();
            }
            catch (Exception ex)
            {
                if (ex.InnerException is RedirectException)
                {
                    Response.Redirect((ex.InnerException as RedirectException).URL, true);
                    Response.End();
                    return;
                }
                Controls.Add(new LiteralControl(XsltDbUtils.GetExceptionMessage(ex)));
            }
        }

        public override void RenderControl(HtmlTextWriter writer)
        {
            renderTimer = Stopwatch.StartNew();
            if (Context.Items["XsltDbGlobalsSession"] == null)
            {
                Context.Items["XsltDbGlobalsSession"] = true;

                string qs;
                if (Page.IsPostBack)
                    qs = XsltDbUtils.GetQSBySlot(XsltDbUtils.globalControlName).ToString(false);
                else
                {
                    string url = HttpContext.Current.Request["XsltDb_js_window_location"] ?? HttpContext.Current.Request.RawUrl;
                    string rawQs = "";
                    if (url.Contains("?"))
                        rawQs = url.Split('?')[1];
                    qs = new MdoQueryString(rawQs).GetGlobalParameters(false);
                }

                writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
                writer.AddAttribute(HtmlTextWriterAttribute.Id, XsltDbUtils.globalControlName);
                writer.AddAttribute(HtmlTextWriterAttribute.Name, XsltDbUtils.globalControlName);
                writer.AddAttribute(HtmlTextWriterAttribute.Value, qs, true);
                writer.RenderBeginTag(HtmlTextWriterTag.Input);
                writer.RenderEndTag();
            }
            try
            {
                base.RenderControl(writer);
            }
            catch (Exception ex)
            {
                writer.Write(XsltDbUtils.GetExceptionMessage(ex));
            }

            const string profileSett = "xsltdb:show-execution-time";
            int PortalID = PortalController.GetCurrentPortalSettings().PortalId;
            Dictionary<string, string> HostSett = Host.GetHostSettingsDictionary();

            if (UserInfo.IsSuperUser &&
                (HostSett.ContainsKey(profileSett) && HostSett[profileSett].ToString() == "yes")
                || PortalController.GetPortalSetting(profileSett, PortalID, string.Empty) == "yes"
                )
            {
                renderTimer.Stop();
                writer.Write("<br/>Execution time (ms): " +
                    (initTimer.ElapsedMilliseconds + preTimer.ElapsedMilliseconds + renderTimer.ElapsedMilliseconds));
            }
            else
                renderTimer.Stop();
        }
    }
}